<?php

namespace mongrove;


/**
 *
 * The LocationField encapsulates a geographic location in Mongo documents.
 * In PHP this field will be represented by an object with a property lat/long.
 * Setting this property can be done by using an array or object with the properties
 * x/y or lat/long.
 *
 * @author Michiel Hakvoort <michiel@hakvoort.it>
 * @author Gido Hakvoort <gido@hakvoort.it>
 *
 */
class LocationField extends AbstractField {

    protected $value;

    /**
     * Specify a LocationField with a default value
     *
     * @param int $default
     */
    public function __construct($default = null) {
    	parent :: __construct();

    	$this->value = array('lat' => 0.0, 'lon' => 0.0);

    	if($default !== null) {
            $this->setValue($default);
    	}
    }

    /**
     * (non-PHPdoc)
     * @see src/mongrove.SimpleField::getValue()
     */
    public function getValue() {
        return $this;
    }

    /**
     * (non-PHPdoc)
     * @see src/mongrove.SimpleField::setValueImpl()
     */
    protected function setValueImpl($value) {
        if(!is_array($value) && !is_object($value)) {
            throw new \Exception("{$value} is not a valid value for this field.");
        }

        $value = (object) $value;

        $lat = null;
        $lon = null;

        // lat = lat || y
        if(isset($value->lat)) {
            $lat = $value->lat;
        } elseif(isset($value->y)) {
            $lat = $value->y;
        }

        // lon = lon || lng || long || x
        if(isset($value->lon)) {
            $lon = $value->lon;
        } elseif(isset($value->lng)) {
            $lon = $value->lng;
        } elseif(isset($value->long)) {
            $lon = $value->long;
        } elseif(isset($value->x)) {
            $lon = $value->x;
        }
        
        if(!is_numeric($lat)) {
            throw new \Exception("{$lat} is not a valid latitude");
        }
        if(!is_numeric($lon)) {
            throw new \Exception("{$lon} is not a valid longitude");
        }

        $lat = (float) $lat;
        $lon = (float) $lon;

        if($lat < -90 || $lat > 90) {
            throw new \Exception("Latitude {$lat} must lie in [-90 .. 90] degrees");
        }

        if($lon <= -180 || $lon > 180) {
            throw new \Exception("Longitude {$lon} must lie (-180 .. 180] degrees");
        }
        
        if($this->value['lon'] === $lon && $this->value['lat'] === $lat) {
            return false;
        }

        $this->value['lat'] = $lat;
        $this->value['lon'] = $lon;
        
        $this->_state |= self :: STATE_NEW;

        return true;
    }

    public function __get($name) {
        $name = mb_strtolower(trim($name));
        
        switch($name) {
            case "lon"  :
            case "long" :
            case "x" :
            case "lng" :
                return $this->value['lon'];
            case "lat" :
            case "y" :
                return $this->value['lat'];
            default :
                throw \Exception("Unknown property '{$name}'");
        }

    }
    
    public function __set($name, $value) {
        $name = mb_strtolower(trim($name));
        
        $lon = $this->lon;
        $lat = $this->lat;
        
        switch($name) {
            case "lon"  :
            case "long" :
            case "x" :
            case "lng" :
                $lon = $value;
                break;
            case "lat" :
            case "y" :
                $lat = $value;
                break;
            default :
                throw \Exception("Unknown property '{$name}'");
        }

        /*
         * Autocheck boundary bonus
         */
        $this->setValue(array('lat' => $lat, 'lon' => $lon));
    }

   /**
    * (non-PHPdoc)
    * @see src/mongrove.Field::hydrate()
    */
    public function hydrate($value) {
        $this->value = $value;
    }
    
    /**
     * (non-PHPdoc)
     * @see src/mongrove.Field::dehydrate()
     */
    public function dehydrate() {
        return $this->value;
    }

    /**
    * (non-PHPdoc)
    * @see src/mongrove.Field::getMutations()
    */
    public function getMutations($path = null, $name = null) {
        $mutations = array();
    
        if($this->isModified()) {
            $path === null ?: $path .= '.';
            $mutations[] = array(Command :: OP_SET => array("{$path}{$name}" => $this->value));
        }
    
        return $mutations;
    }
    
    /**
     * (non-PHPdoc)
     * @see src/mongrove.Field::rewriteQuery()
     */
    public function rewriteQuery(array $partialQuery) {
        /*
         * Do not rewrite by default
        */
        return $partialQuery;
    }
    

}
